/**
 * @author erond
 */

package slave;

/**
 * Classe per la creazione dei thread necessari per la comunicazione dello SLAVE con il MASTER.
 * @author erond
 */

import java.net.*;
import java.util.EventObject;
import javax.swing.event.EventListenerList;
import commonUtility.XmlHandler;


import slave.SlaveStreamProvider;
import slave.SlaveStreamProviderListener;
import slave.SlaveStreamProvider.ChangeCodingEvent;
import slave.SlaveStreamProvider.LambdaComputationEvent;
import slave.SlaveStreamProvider.TransferJobXmlReadyEvent;


public class SlaveComunicator extends Thread implements SlaveStreamProviderListener{

	private int masterJobServerPort;
	private int slaveDeleteServerPort_after;
	private int slaveDeleteServerPort_before;
	private InetAddress multicastIP = null;
	
	/**
	 * @return the masterJobServerPort
	 */
	public int getMasterJobServerPort() {
		return masterJobServerPort;
	}


	/**
	 * @return the slaveDeleteServerPort_after
	 */
	public int getSlaveDeleteServerPort_after() {
		return slaveDeleteServerPort_after;
	}


	/**
	 * @return the slaveDeleteServerPort_before
	 */
	public int getSlaveDeleteServerPort_before() {
		return slaveDeleteServerPort_before;
	}


	/**
	 * @return the multicastIP
	 */
	public InetAddress getMulticastIP() {
		return multicastIP;
	}


	/**
	 * @return the masterJobServerIP
	 */
	public InetAddress getMasterJobServerIP() {
		return masterJobServerIP;
	}


	public InetAddress masterJobServerIP = null;
	public static EventListenerList listenerList = null;
	public static boolean lambda_flag=true;
	public boolean wait = false;
	public static SlaveJobClientThread slaveJobClientThread = null;
	public static SlaveDeleteServerThread slaveDeleteServerThread = null;
	public MulticastServer xmlPublisher = null;
	public XmlHandler xmlSynchronized ;
	private int multicastPort;
	
	public int getMulticastPort() {
		return multicastPort;
	}
	
	/**
	 * Se tutto questo viene integrato in un unico "slave", con la parte di invio,
	 * il costruttore di SlaveGateway necessiterà anche l'oggetto che verrà condiviso
	 * Gateway g.
	 */

	public SlaveStreamProvider sm = null;

	/**
	 * ********************** EVENT HANDLER *****************************
	 */

	// This methods allows classes to register for Gateway Event
	public void addSlaveComunicatorListener(SlaveComunicatorListener listener) {
		listenerList.add(SlaveComunicatorListener.class, listener);
	}


	// This methods allows classes to unregister for Gateway Event
	public void removeSlaveComunicatorListener(SlaveComunicatorListener listener) {
		listenerList.remove(SlaveComunicatorListener.class, listener);
	}

	/**
	 * @author erond
	 * Evento per la notifica di JOB ready
	 */ 
	@SuppressWarnings("serial")
	public class JobReadyEvent extends EventObject {
		
		private SlaveStreamProvider slaveManager = null;
		
		public JobReadyEvent(SlaveComunicator source, SlaveStreamProvider thread) {
			super(source);
			this.slaveManager=thread;
		}
		
		public SlaveStreamProvider getSlaveManager() {
			return slaveManager;
		}
		
	}

	// This private class is used to fire Gateway Event
	public void fireJobReadyEvent(JobReadyEvent evt) {
		Object[] listeners = listenerList.getListenerList();
		// Each listener occupies two elements - the first is the listener class
		// and the second is the listener instance
		for (int i=0; i<listeners.length; i+=2) {
			if (listeners[i]==SlaveComunicatorListener.class) {
				((SlaveComunicatorListener)listeners[i+1]).jobReady(evt);
			}
		}     
	}

	/**
	 * @author erond
	 * Evento per la notifica di inquiry ready
	 */ 
	@SuppressWarnings("serial")
	public class InquiryReadyEvent extends EventObject {
		public  InquiryReadyEvent(SlaveComunicator source) {
			super(source);		
		}
	}

	// This private class is used to fire Gateway Event
	public void fireInquiryReadyEvent(InquiryReadyEvent evt) {
		System.out.println("SlaveComunicator: lancio il fireInquiryReady event..");
		Object[] listeners = listenerList.getListenerList();
		// Each listener occupies two elements - the first is the listener class
		// and the second is the listener instance
		for (int i=0; i<listeners.length; i+=2) {
			if (listeners[i]==SlaveComunicatorListener.class) {
				((SlaveComunicatorListener)listeners[i+1]).inquiryReady(evt);
			}
		}     
	}


	/**
	 * @author erond
	 * Evento per la notifica di delete.txt ready
	 */ 
	@SuppressWarnings("serial")
	public class DeleteReadyEvent extends EventObject {

		private int fileID = 0;
		private SlaveStreamProvider slaveManager = null;

		public DeleteReadyEvent(SlaveComunicator source,int fileID, SlaveStreamProvider thread) {
			super(source);		
			this.fileID=fileID;
			this.slaveManager=thread;
		}
		public int getFileId() {
			return fileID;
		}
		
		public SlaveStreamProvider getSlaveManager() {
			return slaveManager;
		}

	}

	// This private class is used to fire Gateway Event
	public void fireDeleteReadyEvent(DeleteReadyEvent evt) {
		Object[] listeners = listenerList.getListenerList();
		// Each listener occupies two elements - the first is the listener class
		// and the second is the listener instance
		for (int i=0; i<listeners.length; i+=2) {
			if (listeners[i]==SlaveComunicatorListener.class) {
				((SlaveComunicatorListener)listeners[i+1]).deleteReady(evt);
				System.err.println("[fireDeleteReadyEvent] nel for, listeners.length="+listeners.length);
			}
		}     
	}


	
	public void changeCoding(ChangeCodingEvent evt) {}


	@Override
	public void lambdaComputationDone(LambdaComputationEvent evt) {
		System.out.println("SlaveCommunicator: Ricevuto lambdaCOMPU");
		//while(!wait) {}
		synchronized (slaveJobClientThread) {
			try {
				slaveJobClientThread.notify();
				System.err.println("SlaveComunicator: fatta la notify su slaveJobClientThread!");
			} catch (Exception e) {
				System.out.println("Errore nella notify lambdacomputationdone");
			}
		}
	}

	@Override
	public void transferJobXmlReady(TransferJobXmlReadyEvent evt, int id, SlaveStreamProvider threadToSynch) {
		System.err.println("DEBUG: ricevuto TransferJobXmlReadyEvent");
		synchronized (threadToSynch) {
			try {
				System.out.println("DEBUG: +"+Thread.currentThread().getName()+": fatta la notify sul thread "+threadToSynch.getName());
				threadToSynch.setSlaveDeleteServerMasterConnectorThread((SlaveDeleteServer_MasterConnectorThread_after)Thread.currentThread(), id);	
				
			} catch (Exception e) {
				System.out.println("Errore nella notify transferjobxmlready");
			}
		}
	}


	/**
	 * @param sm = oggetto SlaveStreamProvider condiviso con tutto il nodo SLAVE
	 * @param multicastIP = indirizzo di multicast sul quale pubblicare l'xml
	 * @param multicastPort = porta sulla quale i multicast client dovranno richiedere il join al gruppo
	 * @param masterJobServerIP = indirizzo tcp per la comunicazione a stream col master
	 * @param masterJobServerPort = porta sulla quale viene effettuato il servizio di job/loading
	 * @param slaveDeleteServerPort_after = porta sulla quale viene comunicata la lista di client da eliminare dal vicino AFTER
	 * @param slaveDeleteServerPort_before porta sulla quale viene comunicata la lista di client da eliminare dal vicino BEFORE
	 * @param xmlSynchronized Riferimento al gestore sincronizzato dei file xml
	 */

	public SlaveComunicator(SlaveStreamProvider sm,InetAddress multicastIP,int multicastPort,InetAddress masterJobServerIP,int masterJobServerPort, int slaveDeleteServerPort_after, int slaveDeleteServerPort_before, XmlHandler xmlSynchronized) {
		this.sm=sm;
		this.multicastIP=multicastIP;
		this.multicastPort=multicastPort;
		this.masterJobServerIP=masterJobServerIP;
		this.masterJobServerPort=masterJobServerPort;
		this.slaveDeleteServerPort_after=slaveDeleteServerPort_after;
		this.slaveDeleteServerPort_before=slaveDeleteServerPort_before;
		this.xmlSynchronized=xmlSynchronized;
		listenerList = new EventListenerList();
	}

	/**
	 * Avvio il thread "padre" per tutto il framework di comunicazione lato slave.
	 * Al suo interno dovrò lanciare il JobThread ed il DeleteThread
	 */
	public void run() {
		System.out.println("SlaveComunicator: avviato...");
		
		try {
			
			sm.getSc(this);

			sm.addSlaveManagerListener(this);

		} catch (Exception e) {
			System.out.println("Errore nell'inizializzazione dello SlaveComunicator. Chiudo!");
			e.printStackTrace();
			System.exit(3);
		}
		slaveJobClientThread = new SlaveJobClientThread(this);	
		slaveJobClientThread.setName("slaveJobClientThread");
		slaveJobClientThread.start();
		
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}

		slaveDeleteServerThread = new SlaveDeleteServerThread(this);
		slaveDeleteServerThread.setName("slaveDeleteServerThread");
		slaveDeleteServerThread.start();
		
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}


		//pubblico l'xml
		xmlPublisher = new MulticastServer(multicastIP, multicastPort, 1000, true, xmlSynchronized);
		xmlPublisher.start();
		
		try { 
			xmlPublisher.join();
		} catch (InterruptedException e1) {
			System.err.println("Terminato il server multicast di pubblicazione xml sullo slave!");
		}

		System.err.println(Thread.currentThread().getName()+": xmlPublisher terminato. Vado in Join sul JobClientThread..");
		try {
			slaveJobClientThread.join();
		} catch (InterruptedException e) {
			System.out.println("\nSlaveComunicator: eccezione nel join col JobClientThread...");
			e.printStackTrace();
		}
		try {
			slaveDeleteServerThread.join();
		} catch (InterruptedException e) {
			System.out.println("\nSlaveComunicator: eccezione nel join col DeleteServerThread...");
			e.printStackTrace();
		}

	}//fine run()

}//fine class SlaveComunicator
